﻿using System;
using System.Collections.Specialized;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Text.RegularExpressions;

namespace MPlayer.DotNet
{
    /// <summary>
    /// Class for managing relations with mplayer application
    /// </summary>
    public static class MPlayerContext
    {
        /// <summary>
        /// Gets or sets a location of mplayer
        /// </summary>
        public static string ExecutablePath { get; set; }

        /// <summary>
        /// Gets or sets a filename of mplayer
        /// </summary>
        public static string ExecutableName { get; set; }

        /// <summary>
        /// Gets a value indicating whether context initialized
        /// </summary>
        public static bool IsInitialized { get; internal set; }

        public static string FileName { get; internal set; }

        /// <summary>
        /// Keep handle for a video output window
        /// </summary>
        private static int _windowHandle;

        /// <summary>
        /// List of startup player commandline options
        /// </summary>
        private static readonly StringCollection Options;

        /// <summary>
        /// Mplayer process
        /// </summary>
        private static Process _mplayer;

        /// <summary>
        /// Video mode keeper
        /// </summary>
        private static readonly MPlayerVideoMode videoMode;

        /// <summary>
        /// Audio mode keeper
        /// </summary>
        private static readonly MPlayerAudioMode audioMode;

        /// <summary>
        /// Video mode
        /// </summary>
        public static MPlayerVideoModeName VideoMode {
            get { return videoMode.Mode; }
            set { videoMode.Mode = value; }
        }

        /// <summary>
        /// Audio mode
        /// </summary>
        public static MPlayerAudioModeName AudioMode
        {
            get { return audioMode.Mode; }
            set { audioMode.Mode = value; }
        }

        /// <summary>
        /// Expression for media details detection
        /// </summary>
        private static readonly Regex ExpressionData;

        /// <summary>
        /// Expression for pregress detection
        /// </summary>
        private static readonly Regex ExpressionProgress;

        /// <summary>
        /// Whether we processing header output
        /// </summary>
        private static bool _isParsingHeader;

        /// <summary>
        /// Initializes static members of the MplayerContext class
        /// </summary>
        static MPlayerContext()
        {
            IsInitialized = false;
            ExecutablePath = string.Empty;
            ExecutableName = "mplayer.exe";
            Options = new StringCollection();
            audioMode = new MPlayerAudioMode();
            videoMode =  new MPlayerVideoMode();
            FileName = string.Empty;

            ExpressionData = new Regex(
                @"^([a-z_]+)=([a-z0-9_\.\,]+)",
                RegexOptions.IgnoreCase | RegexOptions.Compiled | RegexOptions.Singleline);
            ExpressionProgress = new Regex(
                @"v:\s+(\d+[\.\,]\d+)\s+([\d/]+)\s+([\d]*)\s+([\d\?]+)%\s+([\d\?]+)%\s+([\d\?]+[\.\,]?[\d\?]*)%",
                RegexOptions.IgnoreCase | RegexOptions.Compiled | RegexOptions.Singleline);
        }

        /// <summary>
        /// Destructor
        /// </summary>
        public static void CloseAll()
        {
            try
            {
                if (_mplayer != null && !_mplayer.HasExited)
                {
                    _mplayer.Kill();
                }
            }
            catch { }
        }

        /// <summary>
        /// Add startup option for player
        /// </summary>
        /// <param name="option">MPlayer commandline option</param>
        public static void AddOption(string option)
        {
            if (string.IsNullOrEmpty(option))
                return;

            if (option.Length > 0 && option.StartsWith("-"))
                option = option.Substring(1);

            Options.Add(option);
        }

        /// <summary>
        /// Remove all options
        /// </summary>
        public static void ClearOptions()
        {
            Options.Clear();
        }

        /// <summary>
        /// Initialize mplayer instance
        /// </summary>
        /// <param name="windowHandle">Window handle for video output</param>
        public static void Initialize(int windowHandle)
        {
            if (windowHandle == 0)
                return;
            _windowHandle = windowHandle;

            if (ExecutablePath == string.Empty)
                ExecutablePath = Directory.GetCurrentDirectory();

            if (ExecutableName == string.Empty)
                ExecutableName = "mplayer.exe";

            var fileName = ExecutablePath + "\\" + ExecutableName;
            if (!File.Exists(fileName))
                return;

            AddOption("nofs");          // disable fullscreen
            AddOption("noquiet");       // print out information
            AddOption("identify");      // print out detailed information
            AddOption("idle");          // wait insead of quit
            AddOption("slave");         // shitch on slave mode for frontend
            AddOption("nomouseinput");  // disable mouse input events

            IsInitialized = true;
        }

        /// <summary>
        /// Open media file
        /// </summary>
        /// <param name="fileName">Media file name</param>
        /// <returns>Result</returns>
        public static bool OpenFile(string fileName)
        {
            if (string.IsNullOrEmpty(fileName) || !File.Exists(fileName))
                return false;

            if (_mplayer != null)
            {
                _mplayer.OutputDataReceived -= OutputDataReceived;
                try
                {
                    _mplayer.Kill();
                }
                catch { }
            }

            var commandArguments = string.Format(
                " -vo {0} -ao {1}",
                videoMode.ModeCommand,
                audioMode.ModeCommand);
            commandArguments = Options.Cast<string>().Aggregate(
                commandArguments,
                (current, option) => current + string.Format(" -{0}", option));
            commandArguments += string.Format(" -wid {0}", _windowHandle);

            _mplayer = new Process
            {
                StartInfo =
                {
                    FileName = ExecutablePath + "\\" + ExecutableName,
                    UseShellExecute = false,
                    RedirectStandardInput = true,
                    RedirectStandardOutput = true,
                    RedirectStandardError = true,
                    CreateNoWindow = true,
                    Arguments = commandArguments + string.Format(" \"{0}\"", fileName)
                }
            };

            try
            {
                _mplayer.Start();
            }
            catch (Exception) { return false; }

            _mplayer.OutputDataReceived += OutputDataReceived;
            _mplayer.ErrorDataReceived += ErrorDataReceived;
            _mplayer.BeginOutputReadLine();
            _mplayer.BeginErrorReadLine();
           
            FileName = fileName;
            _isParsingHeader = true;

            return _mplayer != null ? true : false;
        }

        /// <summary>
        /// Error data handler
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        static void ErrorDataReceived(object sender, DataReceivedEventArgs e)
        {
            //Debug.WriteLine(e.Data);
        }

        /// <summary>
        /// Output data handler
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        static void OutputDataReceived(object sender, DataReceivedEventArgs e)
        {
            Match match;
            Debug.WriteLine(e.Data);
            if (_isParsingHeader)
            {
                match = ExpressionData.Match(e.Data);
                if (match.Groups.Count == 3)
                {
                    ProcessDetails(match.Groups[1].Value, match.Groups[2].Value);
                    return;
                }
            }

            match = ExpressionProgress.Match(e.Data);
            if (match.Groups.Count == 7)
            {
                ProcessProgress(match.Groups[1].Value);
                _isParsingHeader = true;
                return;
            }
        }

        static void ProcessProgress(string time)
        {
            
        }

        /// <summary>
        /// Process details
        /// </summary>
        /// <param name="key">Key string</param>
        /// <param name="value">Value string</param>
        static void ProcessDetails(string key, string value)
        {
            switch (key)
            {
                case "ID_VIDEO_FORMAT":
                    break;
                case "ID_VIDEO_BITRATE":
                    break;
                case "ID_VIDEO_WIDTH":
                    break;
                case "ID_VIDEO_HEIGHT":
                    break;
                case "ID_VIDEO_FPS":
                    break;
                case "ID_VIDEO_ASPECT":
                    break;
                case "ID_AUDIO_FORMAT":
                    break;
                case "ID_AUDIO_BITRATE":
                    break;
                case "ID_AUDIO_RATE":
                    break;
                case "ID_AUDIO_NCH":
                    break;
                case "ID_LENGTH":
                    break;
                case "ID_SEEKABLE":
                    break;
            }
        }

        /// <summary>
        /// Send command to player
        /// </summary>
        /// <param name="command">Command</param>
        /// <returns>Result</returns>
        public static bool SendCommand(string command)
        {
            if (IsInitialized && _mplayer != null && !_mplayer.HasExited)
            {
                try
                {
                    _mplayer.StandardInput.Write(command + "\n");
                    return true;
                }
                catch
                {
                    return false;
                }
            }
            return false;
        }
    }
}
